package de.fuberlin.processing.who.cancer.utils.graphics;

import processing.core.PApplet;


public class BezierRing {

	// @see (http://itc.ktu.lt/itc354/Riskus354.pdf)
	private static final float KAPPA = ( 4.0f * ( ( (float) Math.sqrt( 2.0f) - 1.0f) / 3.0f));

	private PApplet p;


	private BezierRing( PApplet papplet) {
		this.p = papplet;
	}


	/**
	 * Berechnet ein Teilstück eines Kreises im Uhrzeigersinn - genauer das im
	 * 4.ten Quadranten - mit einer Bezierkurve.
	 * 
	 * @param cx
	 *          X-Koordinate des Mittelpunktes
	 * @param cy
	 *          Y-Koordinate des Mittelpunktes
	 * @param r
	 *          Radius des Kreises
	 * @param countOfSegments
	 *          Anzahl der Segmente in die das 4te Quadrant unterteilt werden
	 *          soll.
	 * @param startSegment
	 *          Segment, bei dem mit dem Zeichnen des Teilkreises begonnen werden
	 *          soll.
	 * @param endSegment
	 *          Segment, bei dem mit dem Zeichnen des Teilkreises gestoppt werden
	 *          soll.
	 * 
	 */
	private float[] calcBezierCircleQuarterQ4CW( float cx, float cy,
			float radius, int countOfSegments, int startSegment, int endSegment) {
		// Segment, bei dem die Bezierkurve anfängt
		float t1 = p.map( startSegment, 0, countOfSegments, 0, 0.5f);
		// Segment, bei dem die Kurve zuende ist. @pre: t1 <= t2
		float t2 = p.map( endSegment, 0, countOfSegments, 0, 0.5f);
		// Berechne Winkel der Schenkel bezogen auf die horizontale
		float phi = p.PI * t1;
		float psi = p.PI * t2;
		// Berechne Winkel zwischen den Schenkeln
		// und davon den Halben.
		float delta = p.abs( psi - phi) / 2;
		// damit lässt sich die Gegenkathete des Dreiecks aus einem
		// der Schenkel und der Winkelhalbierenden mit dem Winkel delta berechnen.
		// Die Strecken zwischen den Ankerpunkten und Kontollpunkten dürfen
		// die Länge der Gegenkathete R nicht überschreiten.
		float R = radius * p.tan( delta);
		// Da die Konstante Kappa nicht ganz genau ist,
		// versuche ein C zu finden
		float C = ( p.abs( p.sin( phi)) + p.abs( p.cos( psi))) * KAPPA / 3;
		// sodass dieses zu Kappa hinzugezählt im
		// relevanten Bereich die Ungenauigkeiten niveliert.
		float kappaC = KAPPA + C;

		// Ankerpunkt, an der die Bezierkurve startet, berechnen
		float ax = cx + radius * p.cos( phi);
		float ay = cy + radius * p.sin( phi);
		// Zugehörigen Kontrollpunkt berechnen
		float _ax = ax - ( R * kappaC * p.sin( phi));
		float _ay = ay + ( R * kappaC * p.cos( phi));
		// Ankerpunkt, an der die Bezierkurve endet, berechnen
		float bx = cx + radius * p.cos( psi);
		float by = cy + radius * p.sin( psi);
		// den dazugehörigen Kontrollpunkt berechnen
		float _bx = bx + ( R * kappaC * p.sin( psi));
		float _by = by - ( R * kappaC * p.cos( psi));
		// Mit diesen Punkten kann nun der Teilkreis gezeichnet werden
		float[] result = { ax, ay, _ax, _ay, _bx, _by, bx, by};
		return result;
	}


	private float[] calcBezierCircleQuarterQ4ACW( float cx, float cy,
			float radius, int countOfSegments, int startSegment, int endSegment) {
		float t1 = p.map( startSegment, 0, countOfSegments, 0, 0.5f);
		float t2 = p.map( endSegment, 0, countOfSegments, 0, 0.5f);
		float phi = p.PI * t2;
		float psi = p.PI * t1;
		float delta = p.abs( psi - phi) / 2;
		float R = radius * p.tan( delta);
		float C = ( p.abs( p.cos( phi)) + p.abs( p.sin( psi))) * KAPPA / 3;
		float kappaC = KAPPA + C;
		float ax = cx + radius * p.cos( phi);
		float ay = cy + radius * p.sin( phi);
		float _ax = ax + ( R * kappaC * p.sin( phi));
		float _ay = ay - ( R * kappaC * p.cos( phi));
		float bx = cx + radius * p.cos( psi);
		float by = cy + radius * p.sin( psi);
		float _bx = bx - ( R * kappaC * p.sin( psi));
		float _by = by + ( R * kappaC * p.cos( psi));
		float[] result = { ax, ay, _ax, _ay, _bx, _by, bx, by};
		return result;
	}


	private float[] calcBezierCircleQuarterQ3CW( float cx, float cy,
			float radius, int countOfSegments, int startSegment, int endSegment) {
		float t1 = p.map( startSegment, 0, countOfSegments, 0.5f, 1);
		float t2 = p.map( endSegment, 0, countOfSegments, 0.5f, 1);
		float phi = p.PI * t2;
		float psi = p.PI * t1;
		float delta = p.abs( psi - phi) / 2;
		float R = radius * p.tan( delta);
		float C = ( p.abs( p.sin( phi)) + p.abs( p.cos( psi))) * KAPPA / 3;
		float kappaC = KAPPA + C;
		float ax = cx + radius * p.cos( psi);
		float ay = cy + radius * p.sin( psi);
		float _ax = ax - ( R * kappaC * p.sin( psi));
		float _ay = ay + ( R * kappaC * p.cos( psi));
		float bx = cx + radius * p.cos( phi);
		float by = cy + radius * p.sin( phi);
		float _bx = bx + ( R * kappaC * p.sin( phi));
		float _by = by - ( R * kappaC * p.cos( phi));
		float[] result = { ax, ay, _ax, _ay, _bx, _by, bx, by};
		return result;
	}


	private float[] calcBezierCircleQuarterQ3ACW( float cx, float cy,
			float radius, int countOfSegments, int startSegment, int endSegment) {
		float t1 = p.map( startSegment, 0, countOfSegments, 0.5f, 1);
		float t2 = p.map( endSegment, 0, countOfSegments, 0.5f, 1);
		float phi = p.PI * t1;
		float psi = p.PI * t2;
		float delta = p.abs( psi - phi) / 2;
		float R = radius * p.tan( delta);
		float C = ( p.abs( p.sin( psi)) + p.abs( p.cos( phi))) * KAPPA / 3;
		float kappaC = KAPPA + C;
		float ax = cx + radius * p.cos( psi);
		float ay = cy + radius * p.sin( psi);
		float _ax = ax + ( R * kappaC * p.sin( psi));
		float _ay = ay - ( R * kappaC * p.cos( psi));
		float bx = cx + radius * p.cos( phi);
		float by = cy + radius * p.sin( phi);
		float _bx = bx - ( R * kappaC * p.sin( phi));
		float _by = by + ( R * kappaC * p.cos( phi));
		float[] result = { ax, ay, _ax, _ay, _bx, _by, bx, by};
		return result;
	}


	private float[] calcBezierCircleQuarterQ2CW( float cx, float cy,
			float radius, int countOfSegments, int startSegment, int endSegment) {
		float t1 = p.map( startSegment, 0, countOfSegments, 1, 1.5f);
		float t2 = p.map( endSegment, 0, countOfSegments, 1, 1.5f);
		float phi = p.PI * t1;
		float psi = p.PI * t2;
		float delta = p.abs( psi - phi) / 2;
		float R = radius * p.tan( delta);
		float C = ( p.abs( p.sin( phi)) + p.abs( p.cos( psi))) * KAPPA / 3;
		float kappaC = KAPPA + C;
		float ax = cx + radius * p.cos( phi);
		float ay = cy + radius * p.sin( phi);
		float _ax = ax - ( R * kappaC * p.sin( phi));
		float _ay = ay + ( R * kappaC * p.cos( phi));
		float bx = cx + radius * p.cos( psi);
		float by = cy + radius * p.sin( psi);
		float _bx = bx + ( R * kappaC * p.sin( psi));
		float _by = by - ( R * kappaC * p.cos( psi));
		float[] result = { ax, ay, _ax, _ay, _bx, _by, bx, by};
		return result;
	}


	private float[] calcBezierCircleQuarterQ2ACW( float cx, float cy,
			float radius, int countOfSegments, int startSegment, int endSegment) {
		float t1 = p.map( startSegment, 0, countOfSegments, 1, 1.5f);
		float t2 = p.map( endSegment, 0, countOfSegments, 1, 1.5f);
		float phi = p.PI * t2;
		float psi = p.PI * t1;
		float delta = p.abs( psi - phi) / 2;
		float R = radius * p.tan( delta);
		float C = ( p.abs( p.sin( psi)) + p.abs( p.cos( phi))) * KAPPA / 3;
		float kappaC = KAPPA + C;
		float ax = cx + radius * p.cos( phi);
		float ay = cy + radius * p.sin( phi);
		float _ax = ax + ( R * kappaC * p.sin( phi));
		float _ay = ay - ( R * kappaC * p.cos( phi));
		float bx = cx + radius * p.cos( psi);
		float by = cy + radius * p.sin( psi);
		float _bx = bx - ( R * kappaC * p.sin( psi));
		float _by = by + ( R * kappaC * p.cos( psi));
		float[] result = { ax, ay, _ax, _ay, _bx, _by, bx, by};
		return result;
	}


	private float[] calcBezierCircleQuarterQ1CW( float cx, float cy,
			float radius, int countOfSegments, int startSegment, int endSegment) {
		float t1 = p.map( startSegment, 0, countOfSegments, 1.5f, 2);
		float t2 = p.map( endSegment, 0, countOfSegments, 1.5f, 2);
		float phi = p.PI * t2;
		float psi = p.PI * t1;
		float delta = p.abs( psi - phi) / 2;
		float R = radius * p.tan( delta);
		float C = ( p.abs( p.sin( phi)) + p.abs( p.cos( psi))) * KAPPA / 3;
		float kappaC = KAPPA + C;
		float ax = cx + radius * p.cos( psi);
		float ay = cy + radius * p.sin( psi);
		float _ax = ax - ( R * kappaC * p.sin( psi));
		float _ay = ay + ( R * kappaC * p.cos( psi));
		float bx = cx + radius * p.cos( phi);
		float by = cy + radius * p.sin( phi);
		float _bx = bx + ( R * kappaC * p.sin( phi));
		float _by = by - ( R * kappaC * p.cos( phi));
		float[] result = { ax, ay, _ax, _ay, _bx, _by, bx, by};
		return result;
	}


	private float[] calcBezierCircleQuarterQ1ACW( float cx, float cy,
			float radius, int countOfSegments, int startSegment, int endSegment) {
		float t1 = p.map( startSegment, 0, countOfSegments, 1.5f, 2);
		float t2 = p.map( endSegment, 0, countOfSegments, 1.5f, 2);
		float phi = p.PI * t1;
		float psi = p.PI * t2;
		float delta = p.abs( psi - phi) / 2;
		float R = radius * p.tan( delta);
		float C = ( p.abs( p.sin( psi)) + p.abs( p.cos( phi))) * KAPPA / 3;
		float kappaC = KAPPA + C;
		float ax = cx + radius * p.cos( psi);
		float ay = cy + radius * p.sin( psi);
		float _ax = ax + ( R * kappaC * p.sin( psi));
		float _ay = ay - ( R * kappaC * p.cos( psi));
		float bx = cx + radius * p.cos( phi);
		float by = cy + radius * p.sin( phi);
		float _bx = bx - ( R * kappaC * p.sin( phi));
		float _by = by + ( R * kappaC * p.cos( phi));
		float[] result = { ax, ay, _ax, _ay, _bx, _by, bx, by};
		return result;
	}


	private boolean between( float value, float lowerBound, float upperBound) {
		return ( value > lowerBound && value <= upperBound);
	}



	
	private int modulo( int theValue, int modValue) {
		if ( theValue < 0) {
			int divValue = theValue / modValue;
			int rest = theValue - divValue;
			return modValue + rest;
		} else
			return theValue % modValue;
	}


	private int moduloToBase( int theValue, int modValue, int baseValue) {
		return modulo( theValue - baseValue, modValue) + baseValue;
	}


	private void bezierVertexFromCoords( float... coords) {
		p.vertex( coords[0], coords[1]);
		p.bezierVertex( coords[2], coords[3], coords[4], coords[5], coords[6],
				coords[7]);
	}


	private float[] copyAndPass( int startSource, int startDest, int copyCount,
			float[] sourceValues, float[] destValues) {
		for ( int i = 0; i < copyCount; i++) {
			destValues[startDest + i] = sourceValues[startSource + i];
		}
		return sourceValues;
	}


	private float[] vertexArcClockwise( float cx, float cy, float radius,
			int countOfSegments, int startSegment, int endSegment) {

		float[] result = new float[4];
		int segmentsPerQuarter = countOfSegments / 4;

		int unitStartSegment = modulo( startSegment, countOfSegments);
		int unitEndSegment = modulo( endSegment, countOfSegments);

		int startInQuarter = 4 - modulo(
				p.floor( ( unitStartSegment) / segmentsPerQuarter), 4);
	
		int endInQuarter = 4 - modulo(
				p.floor( ( unitEndSegment - 1.0f) / segmentsPerQuarter < 0 ? 3 : ( unitEndSegment - 1) / segmentsPerQuarter), 4);
		int normalizedStartSegment = unitStartSegment % segmentsPerQuarter;
		int normalizedEndSegment = moduloToBase( unitEndSegment, segmentsPerQuarter, 1);
		
		boolean started = false;
		boolean ended = true;

		int quarterCounter = 0;

		boolean sameQuarter = startInQuarter == endInQuarter && between( endSegment -startSegment, -1, segmentsPerQuarter); 
		
		// start in Quarter 4
		if ( between( startInQuarter, 3, 4)) {
			if ( sameQuarter) {
				bezierVertexFromCoords( copyAndPass(
						0,
						0,
						2,
						calcBezierCircleQuarterQ4CW( cx, cy, radius, segmentsPerQuarter,
								normalizedStartSegment, normalizedEndSegment), result));
				started = false;
				ended = true;
			} else {
				bezierVertexFromCoords( copyAndPass(
						0,
						0,
						2,
						calcBezierCircleQuarterQ4CW( cx, cy, radius, segmentsPerQuarter,
								normalizedStartSegment, segmentsPerQuarter), result));
				started = true;
				ended = false;
			}
			quarterCounter = 4;
		} else

		// start in Quarter 3
		if ( between( startInQuarter, 2, 3)) {
			if ( sameQuarter) {
				bezierVertexFromCoords( copyAndPass(
						0,
						0,
						2,
						calcBezierCircleQuarterQ3CW( cx, cy, radius, segmentsPerQuarter,
								normalizedStartSegment, normalizedEndSegment), result));
				started = false;
				ended = true;
			} else {
				bezierVertexFromCoords( copyAndPass(
						0,
						0,
						2,
						calcBezierCircleQuarterQ3CW( cx, cy, radius, segmentsPerQuarter,
								normalizedStartSegment, segmentsPerQuarter), result));
				started = true;
				ended = false;
			}
			quarterCounter = 3;
		} else

		// start in Quarter 2
		if ( between( startInQuarter, 1, 2)) {
			if ( sameQuarter) {
				bezierVertexFromCoords( copyAndPass(
						0,
						0,
						2,
						calcBezierCircleQuarterQ2CW( cx, cy, radius, segmentsPerQuarter,
								normalizedStartSegment, normalizedEndSegment), result));
				started = false;
				ended = true;
			} else {
				bezierVertexFromCoords( copyAndPass(
						0,
						0,
						2,
						calcBezierCircleQuarterQ2CW( cx, cy, radius, segmentsPerQuarter,
								normalizedStartSegment, segmentsPerQuarter), result));
				started = true;
				ended = false;
			}
			quarterCounter = 2;
		} else

		// start in Quarter 1
		if ( between( startInQuarter, 0, 1)) {
			if ( sameQuarter) {
				bezierVertexFromCoords( copyAndPass(
						0,
						0,
						2,
						calcBezierCircleQuarterQ1CW( cx, cy, radius, segmentsPerQuarter,
								normalizedStartSegment, normalizedEndSegment), result));
				started = false;
				ended = true;
			} else {
				bezierVertexFromCoords( copyAndPass(
						0,
						0,
						2,
						calcBezierCircleQuarterQ1CW( cx, cy, radius, segmentsPerQuarter,
								normalizedStartSegment, segmentsPerQuarter), result));
				started = true;
				ended = false;
			}
			quarterCounter = 1;
		}

		if ( started) {
			while ( !ended) {
				if ( quarterCounter == 1) {
					if ( between( endInQuarter, 3, 4)) {
						bezierVertexFromCoords( copyAndPass(
								6,
								2,
								2,
								calcBezierCircleQuarterQ4CW( cx, cy, radius,
										segmentsPerQuarter, 0, normalizedEndSegment), result));
						ended = true;
						break;
					} else {
						bezierVertexFromCoords( calcBezierCircleQuarterQ4CW( cx, cy,
								radius, segmentsPerQuarter, 0, segmentsPerQuarter));
					}
					quarterCounter = 4;
				}

				if ( quarterCounter == 4) {
					if ( between( endInQuarter, 2, 3)) {
						bezierVertexFromCoords( copyAndPass(
								6,
								2,
								2,
								calcBezierCircleQuarterQ3CW( cx, cy, radius,
										segmentsPerQuarter, 0, normalizedEndSegment), result));
						ended = true;
						break;
					} else {
						bezierVertexFromCoords( calcBezierCircleQuarterQ3CW( cx, cy,
								radius, segmentsPerQuarter, 0, segmentsPerQuarter));
					}
					quarterCounter = 3;
				}

				if ( quarterCounter == 3) {
					if ( between( endInQuarter, 1, 2)) {
						bezierVertexFromCoords( copyAndPass(
								6,
								2,
								2,
								calcBezierCircleQuarterQ2CW( cx, cy, radius,
										segmentsPerQuarter, 0, normalizedEndSegment), result));
						ended = true;
						break;
					} else {
						bezierVertexFromCoords( calcBezierCircleQuarterQ2CW( cx, cy,
								radius, segmentsPerQuarter, 0, segmentsPerQuarter));
					}
					quarterCounter = 2;
				}

				if ( quarterCounter == 2) {
					if ( between( endInQuarter, 0, 1)) {
						bezierVertexFromCoords( copyAndPass(
								6,
								2,
								2,
								calcBezierCircleQuarterQ1CW( cx, cy, radius,
										segmentsPerQuarter, 0, normalizedEndSegment), result));
						ended = true;
						break;
					} else {
						bezierVertexFromCoords( calcBezierCircleQuarterQ1CW( cx, cy,
								radius, segmentsPerQuarter, 0, segmentsPerQuarter));
					}
				}
				quarterCounter = 1;
			}
		}
		return result;
	}


	float[] vertexArcAntiClockwise( float cx, float cy, float radius,
			int countOfSegments, int startSegment, int endSegment) {

		float[] result = new float[4];
		int segmentsPerQuarter = countOfSegments / 4;

		int unitStartSegment = modulo( startSegment, countOfSegments);
		int unitEndSegment = modulo( endSegment, countOfSegments);

		
		int startInQuarter = 4 - modulo(
				p.floor( ( unitEndSegment - 1.0f) / segmentsPerQuarter < 0 ? 3 : ( unitEndSegment - 1) / segmentsPerQuarter), 4);
		int endInQuarter = 4 - modulo(
				p.floor( ( unitStartSegment) / segmentsPerQuarter), 4);
		
	
		int normalizedStartSegment = unitStartSegment % segmentsPerQuarter;
		int normalizedEndSegment = moduloToBase( unitEndSegment, segmentsPerQuarter, 1);
		
		boolean started = false;
		boolean ended = true;

		boolean sameQuarter = startInQuarter == endInQuarter && between( endSegment -startSegment, -1, segmentsPerQuarter); 

		int quarterCounter = 0;

		// start in Quarter 4
		if ( between( startInQuarter, 3, 4)) {
			if ( sameQuarter) {
				bezierVertexFromCoords( copyAndPass(
						0,
						0,
						2,
						calcBezierCircleQuarterQ4ACW( cx, cy, radius, segmentsPerQuarter,
								normalizedStartSegment, normalizedEndSegment), result));
				started = false;
				ended = true;
			} else {
				bezierVertexFromCoords( copyAndPass(
						0,
						0,
						2,
						calcBezierCircleQuarterQ4ACW( cx, cy, radius, segmentsPerQuarter,
								0, normalizedEndSegment), result));
				started = true;
				ended = false;
			}
			quarterCounter = 4;
		} else

		// start in Quarter 3
		if ( between( startInQuarter, 2, 3)) {
			if ( sameQuarter) {
				bezierVertexFromCoords( copyAndPass(
						0,
						0,
						2,
						calcBezierCircleQuarterQ3ACW( cx, cy, radius, segmentsPerQuarter,
								normalizedStartSegment, normalizedEndSegment), result));
				started = false;
				ended = true;
			} else {
				bezierVertexFromCoords( copyAndPass(
						0,
						0,
						2,
						calcBezierCircleQuarterQ3ACW( cx, cy, radius, segmentsPerQuarter,
								0, normalizedEndSegment), result));
				started = true;
				ended = false;
			}
			quarterCounter = 3;
		} else

		// start in Quarter 2
		if ( between( startInQuarter, 1, 2)) {
			if ( sameQuarter) {
				bezierVertexFromCoords( copyAndPass(
						0,
						0,
						2,
						calcBezierCircleQuarterQ2ACW( cx, cy, radius, segmentsPerQuarter,
								normalizedStartSegment, normalizedEndSegment), result));
				started = false;
				ended = true;
			} else {
				bezierVertexFromCoords( copyAndPass(
						0,
						0,
						2,
						calcBezierCircleQuarterQ2ACW( cx, cy, radius, segmentsPerQuarter,
								0, normalizedEndSegment), result));
				started = true;
				ended = false;
			}
			quarterCounter = 2;
		} else

		// start in Quarter 1
		if ( between( startInQuarter, 0, 1)) {
			if ( sameQuarter) {
				bezierVertexFromCoords( copyAndPass(
						0,
						0,
						2,
						calcBezierCircleQuarterQ1ACW( cx, cy, radius, segmentsPerQuarter,
								normalizedStartSegment, normalizedEndSegment), result));
				started = false;
				ended = true;
			} else {
				bezierVertexFromCoords( copyAndPass(
						0,
						0,
						2,
						calcBezierCircleQuarterQ1ACW( cx, cy, radius, segmentsPerQuarter,
								0, normalizedEndSegment), result));
				started = true;
				ended = false;
			}
			quarterCounter = 1;
		}

		if ( started) {
			while ( !ended) {
				if ( quarterCounter == 3) {
					if ( between( endInQuarter, 3, 4)) {
						bezierVertexFromCoords( copyAndPass(
								6,
								2,
								2,
								calcBezierCircleQuarterQ4ACW( cx, cy, radius,
										segmentsPerQuarter, normalizedStartSegment,
										segmentsPerQuarter), result));
						ended = true;
						break;
					} else {
						bezierVertexFromCoords( calcBezierCircleQuarterQ4ACW( cx, cy,
								radius, segmentsPerQuarter, 0, segmentsPerQuarter));
					}
					quarterCounter = 4;
				}

				if ( quarterCounter == 4) {
					if ( between( endInQuarter, 0, 1)) {
						bezierVertexFromCoords( copyAndPass(
								6,
								2,
								2,
								calcBezierCircleQuarterQ1ACW( cx, cy, radius,
										segmentsPerQuarter, normalizedStartSegment,
										segmentsPerQuarter), result));
						ended = true;
						break;
					} else {
						bezierVertexFromCoords( calcBezierCircleQuarterQ1ACW( cx, cy,
								radius, segmentsPerQuarter, 0, segmentsPerQuarter));
					}
					quarterCounter = 1;
				}

				if ( quarterCounter == 1) {
					if ( between( endInQuarter, 1, 2)) {
						bezierVertexFromCoords( copyAndPass(
								6,
								2,
								2,
								calcBezierCircleQuarterQ2ACW( cx, cy, radius,
										segmentsPerQuarter, normalizedStartSegment,
										segmentsPerQuarter), result));
						ended = true;
						break;
					} else {
						bezierVertexFromCoords( calcBezierCircleQuarterQ2ACW( cx, cy,
								radius, segmentsPerQuarter, 0, segmentsPerQuarter));
					}
					quarterCounter = 2;
				}

				if ( quarterCounter == 2) {
					if ( between( endInQuarter, 2, 3)) {
						bezierVertexFromCoords( copyAndPass(
								6,
								2,
								2,
								calcBezierCircleQuarterQ3ACW( cx, cy, radius,
										segmentsPerQuarter, normalizedStartSegment,
										segmentsPerQuarter), result));
						ended = true;
						break;
					} else {
						bezierVertexFromCoords( calcBezierCircleQuarterQ3ACW( cx, cy,
								radius, segmentsPerQuarter, 0, segmentsPerQuarter));
					}
					quarterCounter = 3;
				}
			}
		}
		return result;
	}


	private void arc( float cx, float cy, float outerRadius, float innerRadius,
			int countOfSegments, int startSegment, int endSegment) {
		countOfSegments = ( countOfSegments / 4) * 4;

		float segmentDiff = p.abs( endSegment - startSegment);
		if ( segmentDiff == countOfSegments) {
			// zeichne Vollkreis
			p.pushStyle();
			p.noStroke();
			p.beginShape();
			vertexArcClockwise( cx, cy, outerRadius, countOfSegments, startSegment,
					endSegment);
			vertexArcAntiClockwise( cx, cy, innerRadius, countOfSegments,
					startSegment, endSegment);
			p.endShape();
			p.popStyle();
			p.pushStyle();
			p.noFill();
			p.ellipseMode( p.RADIUS);
			p.ellipse( cx, cy, outerRadius, outerRadius);
			p.ellipse( cx, cy, innerRadius, innerRadius);
			p.popStyle();
		} else {
			// zeichne Teilkreis
			p.beginShape();
			float[] outerAnkerCoords = vertexArcClockwise( cx, cy, outerRadius,
					countOfSegments, startSegment, endSegment);
			float[] innerAnkerCoords = vertexArcAntiClockwise( cx, cy, innerRadius,
					countOfSegments, startSegment, endSegment);
			p.vertex( outerAnkerCoords[0], outerAnkerCoords[1]);
			p.endShape();
		}
	}


	public static void arc( PApplet p, float cx, float cy, float outerRadius,
			float innerRadius, int countOfSegments, int startSegment, int endSegment) {
		BezierRing r = new BezierRing( p);
		r.arc( cx, cy, outerRadius, innerRadius, countOfSegments, startSegment,
				endSegment);
	}


	public static void arc( PApplet p, float cx, float cy, float outerRadius,
			float innerRadius, int countOfSegments, int... fractionSegments) {
		BezierRing r = new BezierRing( p);
		for ( int i = 0; i < fractionSegments.length - 1; i++) {
			r.arc( cx, cy, outerRadius, innerRadius, countOfSegments,
					fractionSegments[i], fractionSegments[i + 1]);
		}
		r.arc( cx, cy, outerRadius, innerRadius, countOfSegments,
				fractionSegments[fractionSegments.length - 1], countOfSegments);

	}


	public static void arc( PApplet p, float cx, float cy, float outerRadius,
			float innerRadius, int countOfSegments, RingSegmentStyle ... fractionSegments) {
		BezierRing r = new BezierRing( p);
		if ( fractionSegments.length == 1) {
			p.pushStyle();
			p.g.style( fractionSegments[0].style);
			r.arc( cx, cy, outerRadius, innerRadius, countOfSegments, fractionSegments[0].startSegment,
	  			fractionSegments[0].startSegment+countOfSegments);	
			p.popStyle();
		} else {
		for ( int i = 0; i < fractionSegments.length; i++) {	
			p.pushStyle();
			p.g.style( fractionSegments[i].style);
			r.arc( cx, cy, outerRadius, innerRadius, countOfSegments, fractionSegments[i].startSegment,
	  			fractionSegments[i].endSegment);	
			p.popStyle();
		} }
	}
}
